/**
* VOValueHolder - ValueHolder which stores value in a ValueObject(JavaBean)
*
* Copyright (c) 2002
* Marty Phelan, All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.taursys.model;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
import com.taursys.model.event.ContentValueChangeEvent;
import com.taursys.model.event.ContentChangeEvent;
import com.taursys.model.event.StructureChangeEvent;
import java.beans.*;
import javax.swing.event.*;
/**
* This is an implementation of ValueHolder which stores value in a ValueObject(JavaBean).
* This ValueHolder can be shared by multiple models and can access all/any
* properties of the ValueObject/JavaBean. For each property accessed, this
* object creates and uses a PropertyAccessor to manage the access process.
* For performance benefits, this class caches the PropertyAccessors after
* they have been created and reuses them for subsequent invocations.
*/
public class VOValueHolder extends ObjectValueHolder
implements PropertyChangeListener {
private HashMap properties = new HashMap();
private Class valueObjectClass;
private boolean changingPropertyValue = false;
/**
* Constructs a new VOValueHolder.
*/
public VOValueHolder() {
}
// ***********************************************************************
// * PROPERTY VALUE ACCESSOR METHODS (PUBLIC)
// ***********************************************************************
/**
* Returns the value of the given property in the obj.
*/
public Object getPropertyValue(String propertyName) throws ModelException {
return getPropertyAccessor(propertyName).getPropertyValue(obj);
}
/**
* Set the value for the given property in the obj.
* Fires a StateChanged event to any listeners.
*/
public void setPropertyValue(String propertyName, Object value) throws ModelException {
changingPropertyValue = true;
try {
getPropertyAccessor(propertyName).setPropertyValue(obj, value);
} finally {
changingPropertyValue = false;
}
if (!isMultiplePropertiesChanging())
fireStateChanged(
new ContentValueChangeEvent(this, propertyName, null, value));
}
// ***********************************************************************
// * MISC ACCESSOR METHODS
// ***********************************************************************
/**
* Returns the java data type for the given property
*/
public int getJavaDataType(String propertyName) throws ModelException {
return getPropertyAccessor(propertyName).getJavaDateType();
}
// ***********************************************************************
// * VALUE OBJECT ACCESSOR METHODS
// ***********************************************************************
/**
* Set the internal object for this holder. Also registers
* this ValueHolder as a bound property change listener if the internal
* object implements the BoundValueObject interface. Finally it fires a
* ContentChangeEvent to all ChangeListeners.
* @param obj the internal object for this holder.
*/
public void setObject(Object obj) {
super.setObject(obj);
}
/**
* Set the internal object for this holder. Also registers
* this ValueHolder as a bound property change listener if the internal
* object implements the BoundValueObject interface. Finally it fires the
* given ChangeEvent to all ChangeListeners (if it is not null).
* @param obj the internal object for this holder.
* @param e ChangeEvent to fire to all ChangeListeners (if not null)
*/
public void setObject(Object obj, ChangeEvent e) {
if (this.obj != null && this.obj instanceof BoundValueObject)
((BoundValueObject)this.obj).removePropertyChangeListener(this);
this.obj = obj;
if (obj != null && obj instanceof BoundValueObject)
((BoundValueObject)obj).addPropertyChangeListener(this);
if (e != null)
fireStateChanged(e);
}
/**
* Set the internal object for this holder. Also registers
* this ValueHolder as a bound property change listener if the internal
* object implements the BoundValueObject interface. Finally it fires a
* ContentChangeEvent to all ChangeListeners.
* @param obj the internal object for this holder.
*/
public void setValueObject(Object obj) {
setObject(obj);
}
/**
* Set the internal object for this holder. Also registers
* this ValueHolder as a bound property change listener if the internal
* object implements the BoundValueObject interface. Finally it fires the
* given ChangeEvent to all ChangeListeners (if it is not null).
* @param obj the internal object for this holder.
* @param e ChangeEvent to fire to all ChangeListeners (if not null)
*/
public void setValueObject(Object obj, ChangeEvent e) {
setObject(obj, e);
}
/**
* Get the internal object for this holder.
* @return the internal object for this holder.
*/
public Object getValueObject() {
return obj;
}
/**
* Sets the class of the value object. Only needed if the obj
* itself can be null. If set, this takes presidence over the actual
* class of the obj.
*/
public void setValueObjectClass(Class newValueObjectClass) {
valueObjectClass = newValueObjectClass;
fireStateChanged(new StructureChangeEvent(this));
}
/**
* Returns the class of the value object. Only needed if the obj
* itself can be null. If set, this takes presidence over the actual
* class of the obj.
*/
public Class getValueObjectClass() {
return valueObjectClass;
}
// ***********************************************************************
// * PROTECTED ACCESSOR METHODS
// ***********************************************************************
/**
* Get the value for the given property in the given obj.
*/
protected Object getPropertyValue(String propertyName, Object vo)
throws ModelException {
return getPropertyAccessor(propertyName).getPropertyValue(vo);
}
/**
* Get the values for the given properties in the given obj.
*/
protected Object[] getPropertyValues(String[] propertyNames, Object vo)
throws ModelException {
// do pre-check
if (propertyNames == null)
throw new ModelException(ModelException.REASON_MULTI_PROPERTY_MISMATCH);
// begin retrieval
Object[] results = new Object[propertyNames.length];
for (int i = 0; i < propertyNames.length; i++) {
results[i] =
getPropertyAccessor(propertyNames[i])
.getPropertyValue(vo);
}
return results;
}
/**
* Set the values for the given properties in the given obj.
* Fires a StateChanged event to any listeners.
*/
protected void setPropertyValues(String propertyName, Object value, Object vo)
throws ModelException {
getPropertyAccessor(propertyName).setPropertyValue(vo, value);
fireStateChanged(new ContentValueChangeEvent(this, propertyName, null, value));
}
/**
* Set the values for the given properties in the given obj.
* Fires a StateChanged event to any listeners.
*/
protected void setPropertyValues(String[] propertyNames, Object[] values, Object vo)
throws ModelException {
// do pre-check
checkArrays(propertyNames, values);
// begin changes
changingPropertyValue = true;
try {
for (int i = 0; i < propertyNames.length; i++) {
getPropertyAccessor(propertyNames[i]).setPropertyValue(vo, values[i]);
}
} finally {
changingPropertyValue = false;
}
fireStateChanged(new ContentValueChangeEvent(this, null, null, null));
}
/**
* Returns existing PropertyAccessor for given propertyName else creates new one.
*/
protected PropertyAccessor getPropertyAccessor(String propertyName)
throws ModelException {
PropertyAccessor pa = (PropertyAccessor)properties.get(propertyName);
if (pa == null) {
// Get or set voClass
if (valueObjectClass == null) {
if (obj == null)
throw new ModelPropertyAccessorException(
ModelPropertyAccessorException.REASON_TARGET_AND_CLASS_ARE_NULL,
valueObjectClass, propertyName);
setValueObjectClass(obj.getClass());
}
pa = new PropertyAccessor(valueObjectClass, propertyName);
properties.put(propertyName, pa);
}
return pa;
}
/**
* Returns the map of PropertyAccessors which have been created so far.
*/
protected Map getPropertyAccessors() {
return properties;
}
// ***********************************************************************
// * PROPERTY CHANGE LISTENER METHODS
// ***********************************************************************
/**
* This method gets called when a bound property is changed.
* Notifies this value holder that a property of the value object has changed.
* This component will, in turn, notify all of its ChangeListeners with a
* ContentValueChangeEvent. The components will NOT be notified twice if the
* change was initiated through this component's setPropertyValue method.
*/
public void propertyChange(PropertyChangeEvent e) {
if (!changingPropertyValue)
fireStateChanged(new ContentValueChangeEvent(
this, e.getPropertyName(), e.getOldValue(), e.getNewValue()));
}
}